/*
 * CopyRight(C) Kenbin
 */

#ifndef LSG_BUDDYFILE_INCLUDED
#define LSG_BUDDYFILE_INCLUDED

/*Comment#
   包含了buddybit的文件存储管理，这种文件采用一定的命名规则：
   <block_size>.<block_count>.<file_seq>

   <block_size>是存储在文件中的数据块的尺寸；
   <block_count>是存储在文件中的数据块的数量，必须是2的倍数；
   <file_seq>是当前文件的序列号，当把若干相同类型（<block_size>
   和<block_count>相同）文件构成一个文件桶，<file_seq>用于标识
   当前文件在文件桶中的序列位置。

   文件包含三个部分：[headblock][buddybit][data_blocks]

   [headblock]是文件头空间块，簿记了一些重要的统计数据：
   0 - 3 byte 以网络字节序存储已经分配的数据块数量；
   4 - 7 byte 以网络字节序存储<block_size>；
   8 - 11 byte 以网络字节序存储<block_count>。

   [buddybit]是数据块的状态记录序列，每个数据块占用2个bit位。

   [data_blocks]是数据块，最大长度为<block_size> * <block_count>
   一般不超过1G。

   新创建的文件只包含[headblock]和[buddybit]，之后按照buddy算法
   分配数据块并增长文件。
*/

namespace LSG {

    class CBuddyBit;

    class CBuddyFile {

	public:
	    enum { HEADBLOCK_SIZE = 4096, RESERVE_SIZE = 16 };

	    CBuddyFile(const char *filename);
	    CBuddyFile(const char *filename, int block_size, int block_count);
	    ~CBuddyFile();

	    /* ok, return buddybit_offset; fail, return < 0 */
	    int Save(char *data, int data_len, int reserve = 0);
	    /* ok, return 0; fail, return < 0 */
	    int Update(int buddybit_offset, int byte_offset, 
		    const char *data, int data_len, int reserve = 0);
	    /* ok, return 0; fail, return < 0 */
	    int Read(int buddybit_offset, char **data, 
		    int *data_len, int *reserve = NULL);
	    /* ok, return 0; fail, return < 0 */
	    int Delete(int buddybit_offset);

	    CBuddyBit *BuddyBit() { return m_buddybit_obj; }

	public:
	    bool Error() const
	    {
		if(m_errid != 0) {
		    return true;
		}

		return false;
	    }

	    const char *ErrMsg()
	    {
		switch(m_errid) {
		    case 1: return "invalid block_size";
		    case 2: return "invalid block_count";
		    case 3: return "file size exceed 2G";
		    case 4: return "invalid file_seq";
		    case 5: return strerror(m_errno);
		    case 6: return "init buddybit fail";
		    case 7: return "load buddybit fail";
		    default: return "undefined";
		}
	    }

	private:
	    int create_buddybit();
	    int load_buddybit();
	    int flush_headblock();

	    int m_fd;
	    int m_block_size;
	    int m_block_count;
	    int m_buddybit_size;
	    CBuddyBit *m_buddybit_obj;

	    int m_errid;
	    int m_errno;
    };

};

#endif
